package batch.status;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.List;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

import common.db.jdbc.Jdbc;

import core.config.Env;
import core.exception.PhysicalException;
import core.exception.ThrowableUtil;

/**
 * ジョブ詳細管理テーブル取得／更新クラス
 *
 * @author Tadashi Nakayama
 * @version 1.0.0
 */
public final class JobDetailStatusImpl implements JobDetailStatus {
	/** ログ出力用クラス */
	private static final Logger LOGGER = LogManager.getLogger(JobDetailStatusImpl.class);

	/** テーブル名 ジョブ詳細管理 */
	private static final String TABLE = Env.getEnv("JobDetail.Table");

	/** カラム名 ジョブ連番 */
	private static final String COL_JOB_SEQ = Env.getEnv("JobDetail.JobSeq");
	/** カラム名 バッチ連番 */
	private static final String COL_BAT_SEQ = Env.getEnv("JobDetail.BatSeq");
	/** カラム名 プロセスID */
	private static final String COL_PID = Env.getEnv("JobDetail.Pid");
	/** カラム名 バッチ名 */
	private static final String COL_BAT_MEI = Env.getEnv("JobDetail.BatMei");
	/** カラム名 バッチステータス */
	private static final String COL_BAT_STS = Env.getEnv("JobDetail.BatSts");
	/** カラム名 開始日時 */
	private static final String COL_KAISHI = Env.getEnv("JobDetail.Kaishi");
	/** カラム名 終了日時 */
	private static final String COL_SHURYO = Env.getEnv("JobDetail.Shuryo");
	/** カラム名 メッセージテキスト */
	private static final String COL_MSG_TXT = Env.getEnv("JobDetail.MsgTxt");

	/**
	 * テーブル名取得
	 *
	 * @return テーブル名
	 */
	@Override
	public String getTableName() {
		return TABLE;
	}

	/**
	 * ジョブ詳細管理状態取得
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @param dseq バッチ連番
	 * @return ジョブ詳細管理状態リスト
	 */
	@Override
	public JobDetail getJobDetail(final Connection conn, final long seq, final int dseq) {
		final String query = String.format(
				"SELECT * FROM %s WHERE %s = ? AND %s = ? ORDER BY %s DESC FOR UPDATE ",
				TABLE, COL_JOB_SEQ, COL_BAT_SEQ, COL_BAT_SEQ);

		try (PreparedStatement psmt = Jdbc.wrap(conn).readonlyStatement(query)) {
			// 読込
			psmt.setLong(1, seq);
			psmt.setInt(2, dseq);

			JobDetail rec = null;
			try (ResultSet rs = psmt.executeQuery()) {
				if (rs.next()) {
					rec = toJobDetailObject(rs);
				}
			}
			return rec;

		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細管理状態取得
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return ジョブ詳細管理状態リスト
	 */
	@Override
	public List<JobDetail> selectJobDetails(final Connection conn, final long seq) {
		final String query = String.format(
				"SELECT * FROM %s WHERE %s = ? ORDER BY %s DESC FOR UPDATE ",
				TABLE, COL_JOB_SEQ, COL_BAT_SEQ);

		// 読込
		LOGGER.debug(query);
		LOGGER.debug("jobseq={}", String.valueOf(seq));

		try (PreparedStatement psmt = Jdbc.wrap(conn).readonlyStatement(query)) {
			psmt.setLong(1, seq);

			List<JobDetail> list = new ArrayList<>();
			try (ResultSet rs = psmt.executeQuery()) {
				while (rs.next()) {
					list.add(toJobDetailObject(rs));
				}
			}
			return list;

		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細管理更新
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @param dseq バッチ連番
	 * @param msg メッセージ
	 * @param status ステータス
	 * @param time 日時
	 * @return 更新レコード数
	 */
	@Override
	public int updateJobDetail(final Connection conn, final long seq, final int dseq,
			final String msg, final JobState status, final Timestamp time) {
		final String sql = String.format(
				"UPDATE %s SET %s = ?, %s = ?, %s = ? WHERE %s = ? AND %s = ? ",
				TABLE, COL_BAT_STS, COL_SHURYO, COL_MSG_TXT, COL_JOB_SEQ, COL_BAT_SEQ);

		try (PreparedStatement psmt = Jdbc.wrap(conn).prepareStatement(sql)) {
			// ジョブ詳細管理テーブル更新
			int i = 1;
			psmt.setInt(i++, status.value());
			psmt.setTimestamp(i++, time);
			psmt.setString(i++, msg);
			psmt.setLong(i++, seq);
			psmt.setInt(i++, dseq);
			return psmt.executeUpdate();

		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細管理新規作成
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @param pid プロセスID
	 * @param name バッチ処理名称
	 * @param time 日時
	 * @return バッチシーケンス番号
	 */
	@Override
	public int insertJobDetail(final Connection conn, final long seq,
			final int pid, final String name, final Timestamp time) {
		final String sql = String.format(
			"INSERT INTO %s (%s, %s, %s, %s, %s, %s, %s, %s) VALUES(?, ?, ?, ?, ?, ?, NULL, NULL)",
			TABLE, COL_JOB_SEQ, COL_BAT_SEQ, COL_PID, COL_BAT_MEI,
			COL_BAT_STS, COL_KAISHI, COL_SHURYO, COL_MSG_TXT);

		int dtlno = 1;
		List<JobDetail> dtllst = selectJobDetails(conn, seq);
		if (!dtllst.isEmpty()) {
			dtlno += dtllst.size();
		}

		try (final PreparedStatement psmt = Jdbc.wrap(conn).prepareStatement(sql)) {
			int i = 1;
			psmt.setLong(i++, seq);
			psmt.setInt(i++, dtlno);
			psmt.setInt(i++, pid);
			psmt.setString(i++, name);
			psmt.setInt(i++, JobState.ID_B_EDIT.value());
			psmt.setTimestamp(i++, time);

			// ジョブ管理テーブル更新
			int ret = psmt.executeUpdate();
			if (ret != 1) {
				throw new IllegalStateException(String.valueOf(ret));
			}
			return dtlno;

		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細管理削除
	 *
	 * @param conn コネクション
	 * @param seq ジョブ連番
	 * @return 削除件数
	 */
	@Override
	public int deleteJobDetail(final Connection conn, final long seq) {
		final String sql = String.format("DELETE FROM %s WHERE %s = ? ", TABLE, COL_JOB_SEQ);

		try (PreparedStatement psmt = Jdbc.wrap(conn).prepareStatement(sql)) {
			psmt.setLong(1, seq);
			return psmt.executeUpdate();
		} catch (SQLException ex) {
			ThrowableUtil.error(ex);
			throw new PhysicalException(ex);
		}
	}

	/**
	 * ジョブ詳細オブジェクト取得
	 *
	 * @param rs 結果セット
	 * @return ジョブ詳細オブジェクト
	 * @throws SQLException SQL例外
	 */
	private JobDetail toJobDetailObject(final ResultSet rs) throws SQLException {
		JobDetail ret = new JobDetail();
		ret.setJobSeq(rs.getLong(COL_JOB_SEQ));
		ret.setBatSeq(rs.getInt(COL_BAT_SEQ));
		ret.setPid(rs.getInt(COL_PID));
		ret.setBatName(rs.getString(COL_BAT_MEI));
		ret.setBatSts(rs.getInt(COL_BAT_STS));
		ret.setStartDtm(rs.getTimestamp(COL_KAISHI));
		ret.setStopDtm(rs.getTimestamp(COL_SHURYO));
		ret.setMsgTxt(rs.getString(COL_MSG_TXT));
		return ret;
	}
}
